package com.yangyouli.unity_demo_server.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;

import com.yangyouli.unity_demo_server.container.Container;
import com.yangyouli.unity_demo_server.server.serializeModel.PlayerStatus;
import com.yangyouli.unity_demo_server.util.Message;
import com.yangyouli.unity_demo_server.util.ThreadLocalHelper;

public class Client implements Runnable, Request, Comparable<Client>{
	public static final String OFFLINE_CLASSNAME = "OfflineRequest";
	// 解析数据错误时，无法得到对应的 requestClassName，就发送响应给该请求类
	private static final String PARSE_DATA_ERR_DEFAULT_REQUEST_NAME = "DefaultRequest";
	
	private int userId = 0;
	private Socket clientSocket; // 客户端的 Socket
	private Server server; // 服务器实例
	private RoomObj roomObj; // 玩家所在的房间
	private Message message = new Message();
	// 上一次接收到心跳连接的时间
	private long heartbeatTime;
	PlayerStatus playerStatus;
	// 在执行完 Controller 中的方法后将 delayResponse 发送给客户端
	// 其 requestClassName 为 执行 Controller 时的 requestClassName
	private String delayResponse = null;
	private List<Client> delayResponseClientList;
	private int addExp;
	private int addMoney;
	private int Hp;
	
	Logger log = Logger.getLogger(Client.class);
	
	public Client (Socket clientSocket, Server server) {
		this.clientSocket = clientSocket;
		this.server = server;
		heartbeatTime = new Date().getTime();
	}
	
	/**
	 * 客户端的 socket 开始
	 */
	public void start () {
		ThreadLocalHelper.setClient(this);
		recive();
	}
	
	/**
	 * 一直准备接收请求
	 */
	public void recive () {
		InputStream is = null;
		try {
			is = clientSocket.getInputStream();
			while (true) {
				// 向数组的指定长度写入数据，不超过数据的剩余长度
				int length = is.read(message.getData(), message.getDataCount(), message.remineSize());
				// 读取到的数据长度为 0，不再发送数据了，关闭连接
				if (length <= 0) {
					closeSocket();
					break;
				}
				// 解析数据
				try {
					message.parseData(length, this);
				} catch (Exception e) {
					// 数据解析失败 客户端发来的数据格式不正确 返回给客户端错误代码501，关闭连接
					sendResponse(PARSE_DATA_ERR_DEFAULT_REQUEST_NAME, "501");
					closeSocket();
					log.error("解析客户端的数据失败", e);
				}
			}
		} catch (IOException e) {
			// 发生异常，关闭连接
			closeSocket();
		}
		
	}
	
	/**
	 * 发送数据给客户端
	 * @param requestClassName
	 * @param data
	 * @throws IOException 
	 */
	public void sendResponse (String requestClassName, String data) {
		OutputStream os = null;
		try {
			os = clientSocket.getOutputStream();
			os.write(Message.packageData(requestClassName, data));
		} catch (IOException e) {
			log.error("发送数据给客户端发生异常", e);
		}
	}
	
	/**
	 * 关闭代理客户端的 socket 与 传入的输入流
	 */
	public void closeSocket () {
		if (clientSocket != null && !clientSocket.isClosed()) {
			server.removePlayerFromRoom(this);
			log.info("客户端" + clientSocket.getInetAddress().getHostAddress() + "断开连接");
			server.removeClientSocket(this);
			try {
				clientSocket.close();
			} catch (IOException e) {
				log.error("关闭 socket 发生异常", e);
			}
		}
	}

	@Override
	public void run() {
		start();
	}

	@Override
	public void handleRequest(String requestClassName, String controllerName, String methodName, String data) {
		try {
			// 得到 controller 对象
			Object controller = Container.getInstance().getController(controllerName);
			
			if (controller == null) {
				sendResponse(requestClassName, "404");
				return;
			}
			
			// 得到 controller 下的某个方法对象
			Method method = Container.getInstance().getControllerMethod(controllerName, methodName);
			
			if (method == null) {
				sendResponse(requestClassName, "405");
				return;
			}
			
			if (data == null || data.isEmpty()) {
				sendResponse(requestClassName, "502");
				return;
			}
			
			// 反射执行方法，获取返回值
			Object returnValue = null;
			returnValue = method.invoke(controller, data);
			
			// 如果返回值不为空，发送数据给客户端
			if (returnValue != null) {
				if (requestClassName.contains("BE-")) {
					// requestClassName 中包含 BE-，则是广播请求，即发送数据给同一房间的其他客户端
					server.sendToRoomExclude(requestClassName, (String)returnValue, this);
				} else if (requestClassName.contains("B-")) {
					// requestClassName 中包含 B-，则是广播请求，即发送数据给同一房间的所有客户端
					server.sendToRoom(requestClassName, (String)returnValue, this);
				} else if (requestClassName.contains("BA-")) {
					// requestClassName 中包含 BA-，则是广播请求，即发送数据给所有在线的用户
					server.sendToAllClients(requestClassName, (String)returnValue);
				} else {
					sendResponse(requestClassName, (String)returnValue);
				}
			}
			
			if (delayResponse != null) {
				for (Client c : delayResponseClientList) {
					c.sendResponse(requestClassName, delayResponse);
				}
				delayResponseClientList = null;
				delayResponse = null;
			}
		} catch (Exception e) {
			log.error("反射执行 controller[" + controllerName + "] 的 [" + methodName + "]方法发生异常", e);
		}
	}

	public long getHeartbeatTime() {
		return heartbeatTime;
	}

	public void setHeartbeatTime(long heartbeatTime) {
		this.heartbeatTime = heartbeatTime;
	}

	public Socket getClientSocket() {
		return clientSocket;
	}

	public void setClientSocket(Socket clientSocket) {
		this.clientSocket = clientSocket;
	}

	public PlayerStatus getPlayerStatus() {
		return playerStatus;
	}

	public void setPlayerStatus(PlayerStatus playerStatus) {
		this.playerStatus = playerStatus;
	}

	public Server getServer() {
		return server;
	}

	public int getUserId() {
		return userId;
	}

	public void setUserId(int userId) {
		this.userId = userId;
	}

	public RoomObj getRoomObj() {
		return roomObj;
	}

	public void setRoomObj(RoomObj roomObj) {
		this.roomObj = roomObj;
	}

	public String getDelayResponse() {
		return delayResponse;
	}

	public void setDelayResponse(String delayResponse) {
		this.delayResponse = delayResponse;
	}

	public List<Client> getDelayResponseClientList() {
		return delayResponseClientList;
	}

	public void setDelayResponseClientList(List<Client> delayResponseClientList) {
		this.delayResponseClientList = delayResponseClientList;
	}

	public int getAddExp() {
		return addExp;
	}

	public void setAddExp(int addExp) {
		this.addExp = addExp;
	}

	public int getAddMoney() {
		return addMoney;
	}

	public void setAddMoney(int addMoney) {
		this.addMoney = addMoney;
	}

	public int getHp() {
		return Hp;
	}

	public void setHp(int hp) {
		Hp = hp;
	}

	@Override
	public int compareTo(Client o) {
		if (this.hashCode() > o.hashCode()) {
			return 1;
		} else if (this.hashCode() < o.hashCode()) {
			return -1;
		} else {
			return 0;
		}
	}
}
